package com.gigalaxy.game.card.phom
{
	import com.gigalaxy.game.card.BaseHandRenderer;
	import com.gigalaxy.game.card.Card;
	import com.gigalaxy.game.card.CardRenderer;
	import com.gigalaxy.game.card.ICardHandComparator;

	public class PhomHandComparator implements ICardHandComparator
	{
		/**
		 <pre>
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+<br />
		 |\v|KK|QQ|JJ|..|..|..|..|..|..|..|..|..|AA|<br />
		 |s\|KK|QQ|JJ|..|..|..|..|..|..|..|..|..|AA|<br />
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+<br />
		 |1♣|13|12|11|10|09|08|07|06|05|04|03|02|01|<br />
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+<br />
		 |2♦|13|12|11|10|09|08|07|06|05|04|03|02|01|<br />
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+<br />
		 |3♥|13|12|11|10|09|08|07|06|05|04|03|02|01|<br />
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+<br />
		 |4♠|13|12|11|10|09|08|07|06|05|04|03|02|01|<br />
		 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+</pre><br />
		 * Note: là 1 mảng 2 chiều<br />
		 * chiều dọc là chất, chiều ngang là giá trị của quân bài. >>> fix vị trí của 52 quân bài vào từng ô tương ứng<br />
		 * Ban đầu tất cả các ô được khởi tạo = 0.
		 */
		public var handData:Vector.<Vector.<PhomCardComparator>>;
		private var rowsInfo:Vector.<int>; // chứa thông tin về số lượng quân bài cùng chất
		private var columnsInfo:Vector.<int> // chứa thông tin về số lượng quân bài cùng giá trị

		private var ROWS:int = 4;
		private var COLUMNS:int = 13;

		public var cardSets:Vector.<CardRenderer>;

		public function PhomHandComparator()
		{
			initData();
		}

		private function initData():void
		{
			handData = new Vector.<Vector.<PhomCardComparator>>();
			rowsInfo = new Vector.<int>();
			columnsInfo = new Vector.<int>();

			for (var i:uint = 0; i < ROWS; i++) // 4 chất
			{
				handData[i] = new Vector.<PhomCardComparator>();
				rowsInfo[i] = 0;

				for (var j:uint = 0; j < COLUMNS; j++) // 13 quân
				{
					handData[i][j] = new PhomCardComparator();

					if (i == 0)
					{
						columnsInfo[j] = 0;
					}
				}
			}
		}

		public function setHandData(cardSets:Vector.<CardRenderer>):void
		{
			var cardValue:int;
			var cardSuit:int;

			this.cardSets = cardSets;

			for (var i:uint = 0; i < cardSets.length; i++)
			{
				if (cardSets[i].onHand)
				{
					cardValue = cardValueToInt(cardSets[i].card.value); // từ 1 - 13
					cardSuit = cardSuitToInt(cardSets[i].card.suit); // từ 1 - 4

					handData[cardSuit - 1][cardValue - 1].value = cardValue;
					rowsInfo[cardSuit - 1]++;
					columnsInfo[cardValue - 1]++;
				}
			}
		}

		/**
		 * groups là array của các string chứa phỏm<br />
		 * trước khi gọi hàm này thì cần reset data
		 */
		public function computeRankBaseOnGroups(groups:Array):void
		{
			if (groups != null)
			{
				var group:String = "";

				for (var i:uint = 0; i < groups.length; i++)
				{
					group = groups[i].toString();

					if (checkPhomGroupValidate(group))
					{
						setGroupRank(group);
					}
				}

				applySortRank();
			}
		}

		/**
		 * set rank cho group bằng cách truyện vào 1 chuỗi string là 1 phỏm group<br />
		 * >>> cần validate bộ phỏm này đã<br />
		 */
		private function setGroupRank(group:String):void
		{
			var cardsString:Array = group.split(" ");

			var card:Card;

			var isSameKind:Boolean = (cardsString[0].toString().charAt(0) == cardsString[1].toString().charAt(0));

			var sumValue:int = 0;
			var i:uint;

			/**
			 * tính tổng giá trị của bộ phỏm
			 */
			for (i = 0; i < cardsString.length; i++)
			{
				card = BaseHandRenderer.stringToCard(cardsString[i]); // có thể bị âm

				sumValue += cardValueToInt(card.value);
			}

			var cardValue:int;
			var cardSuit:int;

			for (i = 0; i < cardsString.length; i++)
			{
				card = BaseHandRenderer.stringToCard(cardsString[i]); // có thể bị âm
				cardValue = cardValueToInt(card.value); // từ 1 - 13
				cardSuit = cardSuitToInt(card.suit); // từ 1 - 4

				if (isSameKind)
				{
					handData[cardSuit - 1][cardValue - 1].kindRemain = sumValue;
				}
				else
				{
					handData[cardSuit - 1][cardValue - 1].straitRemain = sumValue;
				}
			}
		}

		private function checkPhomGroupValidate(phomGroup:String):Boolean
		{
			// TODO check phỏm validate thực sự

			var res:Boolean = true;

			if (phomGroup == null || phomGroup == '' || phomGroup == " ")
			{
				res = false;
			}

			return res;
		}

		/**
		 * hàm này phải gọi ngay trước hàm sort ở base hand !!!
		 */
		public function computeRank():void
		{
			var maxStrait:uint = maxStraitGroups();
			var maxKinds:uint = maxKindGroups();

			if (maxStrait > maxKinds) // prefer phỏm dọc hơn phỏm ngang >> nếu = nhau thì tính phỏm dọc trước
			{
				setStraitGroupRank();

				if (maxKinds > 0) // chỉ set kind khi > 0
				{
					//setKindGroupRank();
					postSetKindGroupRank(); // need TEST
				}
			}
			else
			{
				setKindGroupRank();

				if (maxStrait > 0) // chỉ set strait khi > 0
				{
					//setStraitGroupRank();
					postSetStraitGroupRank(); // need TEST
				}
			}

			applySortRank();
		}

		private function applySortRank():void
		{

			var cardValue:int;
			var cardSuit:int;

			for (var i:uint = 0; i < cardSets.length; i++)
			{
				if (cardSets[i].onHand)
				{
					cardValue = cardValueToInt(cardSets[i].card.value); // từ 1 - 13
					cardSuit = cardSuitToInt(cardSets[i].card.suit); // từ 1 - 4

					if (Math.max(handData[cardSuit - 1][cardValue - 1].kindRemain, handData[cardSuit - 1][cardValue - 1].straitRemain) != 0)
					{
						if (handData[cardSuit - 1][cardValue - 1].kindRemain > handData[cardSuit - 1][cardValue - 1].straitRemain)
						{
							/*
							nhân với 99 để tránh trường hợp có 1 phỏm dọc và 1 phỏm ngang cùng giá trị >>> phỏm dọc đứng trước phỏm ngang
							nhân với card value để tránh trường hợp có 2 phỏm dọc cùng giá trị >>> phỏm nào có giá trị mặt card lớn hơn thì đứng trước
							//*/
							cardSets[i].card.sortRank = -99 * cardValue * (Math.max(handData[cardSuit - 1][cardValue - 1].kindRemain, handData[cardSuit - 1][cardValue - 1].straitRemain) + handData[cardSuit - 1][cardValue - 1].value);
						}
						else // phỏm ngang
						{
							/*
							nhân với card suit để tránh trường hợp có 2 phỏm ngang cùng giá trị
							//*/
							cardSets[i].card.sortRank = -cardSuit * (Math.max(handData[cardSuit - 1][cardValue - 1].kindRemain, handData[cardSuit - 1][cardValue - 1].straitRemain) + handData[cardSuit - 1][cardValue - 1].value);
						}
					}
				}
			}
		}

		/**
		 * strait là các cards có cùng chất và liền nhau<br />
		 * hàm này sẽ set rank cho những bộ phỏm có cùng chất<br />
		 * VD: A♥ K♥ Q♥ J♥ 10♥ 9♥ 8♥ 7♥ 6♥ 5♥ 4♥ 3♥ 2♥<br />
		 */
		private function setStraitGroupRank():void
		{
			/*
			ở đây test với bộ phỏm như sau
			A♥ K♥ Q♥ J♥ -- 9♥ 8♥ -- 6♥ 5♥ 4♥ 3♥ 2♥
			//*/

			var numCard:int;
			var i:int;
			var j:int;
			var k:int;
			var sumValue:int = 0; // tổng giá trị rank của bộ phỏm

			for (i = 0; i < ROWS; i++)
			{
				for (j = 0; j < COLUMNS; j++)
				{
					if (handData[i][j].value != 0) // nếu card rank tại cột đó !=0 thì tăng số card trong bộ lên 1
					{
						numCard++;

						sumValue += handData[i][j].value;
					}
					else // nếu rank == 0 tức là đã tới điểm cuối của chuỗi phỏm
					{
						if (numCard >= 3) // có phỏm
						{
							/*
							cập nhật giá trị mới cho phỏm ngang (số card cần cập nhật là numCard từ ngay trước card trống trở về trước)
							//*/
							for (k = 1; k <= numCard; k++)
							{
								handData[i][j - k].straitRemain = sumValue - handData[i][j - k].value; // trừ đi giá trị của chính card đó thì sẽ ra remain
							}
						}

						numCard = 0;
					}
				}
			}
		}

		/**
		 * xử lý tính toán phỏm ngang sau khi đã tính toán phỏm dọc
		 */
		private function postSetStraitGroupRank():void
		{
			var numCard:int;
			var i:int;
			var j:int;
			var k:int;
			var l:int;
			var sumKindRemain:int = 0; // tổng giá trị kind remain (phỏm dọc) của bộ phỏm
			var sumStraitRemain:int = 0; // tổng giá trị rank của bộ phỏm
			var sumValue:int = 0;

			for (i = 0; i < ROWS; i++)
			{
				for (j = 0; j < COLUMNS; j++)
				{
					if (handData[i][j].value != 0) // nếu card rank tại cột đó !=0 thì tăng số card trong bộ lên 1
					{
						numCard++;
						sumValue += handData[i][j].value;

						if (handData[i][j].kindRemain != 0 && handData[i][j].kindRemain < 3 * handData[i][j].value) // chỉ cộng kind remain != 0 & số quân bài ở trong cột đó < 4. nếu mà == 4 thì sẽ có thể bỏ qua mà không vấn đề gì
						{
							sumKindRemain += handData[i][j].kindRemain;

							if (numCard > 3) // có từ 4 quân trở lên
							{
							}
						}
						else // chỉ cộng strait remain nếu kind remain == 0 !!! (những quân bài lẻ)
						{
							sumStraitRemain += handData[i][j].value;
						}
					}
					else // nếu value == 0 tức là đã tới điểm cuối của chuỗi phỏm
					{
						if (numCard >= 3) // có phỏm 
						{
							if (sumStraitRemain > sumKindRemain) // sum straint remain > sum kind remain thì mới chuyển phỏm
							{
								/*
								cập nhật giá trị mới cho phỏm ngang (số card cần cập nhật là numCard từ ngay trước card trống trở về trước)
								//*/
								for (k = 1; k <= numCard; k++)
								{
									/*
									card này còn nằm trong 1 phỏm dọc khác >>> cập nhật giá trị cho phỏm dọc đó
									//*/
									if (handData[i][j - k].kindRemain != 0)
									{
										for (l = 0; l < ROWS; l++)
										{
											if (handData[l][j - k].value > 0)
											{
												if (handData[l][j - k].kindRemain >= 3 * handData[l][j - k].value) // nếu có 4 cây >>> bỏ 1 cây còn 3 cây vẫn là phỏm
												{
													handData[l][j - k].kindRemain = handData[l][j - k].value * 2;
												}
												else
												{
													handData[l][j - k].kindRemain = 0;
												}
											}
										}

										handData[i][j - k].kindRemain = 0; // quân bài bị bỏ ra thì kind remain cũng set về 0
									}

									handData[i][j - k].straitRemain = sumValue - handData[i][j - k].value; // cập nhật remain ngang >>> trừ đi giá trị của chính card đó thì sẽ ra remain
								}
							}
						}

						numCard = 0;
						sumKindRemain = 0;
						sumStraitRemain = 0;
						sumValue = 0;
					}
				}
			}
		}

		/**
		 * kind là các cards có cùng giá trị<br />
		 * hàm này sẽ set rank cho những bộ phỏm mà cùng giá trị<br />
		 * VD: K♣ K♦ K♥ | K♣ K♦ K♥ K♠
		 */
		private function setKindGroupRank():void
		{
			/*
			số cards trong 1 row. Nếu là 3 hoặc 4 >>> có phỏm
			*/
			var numCard:int;
			var i:uint;
			var j:uint;

			for (i = 0; i < COLUMNS; i++) // duyệt theo cột
			{
				numCard = columnsInfo[i]; // số card trong bộ = 0

				if (numCard >= 3) // chỉ set những cột có từ 3 card trở lên trong bộ >>> có phỏm
				{
					// tăng remain của tất cả card trong bộ lên (numcard - 1) * rank do remain là giá trị của tổng card còn lại trong bộ cùng với card bị bỏ ra
					for (j = 0; j < ROWS; j++)
					{
						if (handData[j][i].value != 0)
						{
							if (numCard >= 3) // == 4 up. Nếu số card dọc từ 4 trở lên thì có thể bỏ bất kỳ 1 card nào ra cũng được >>> remain = 0
							{
								handData[j][i].kindRemain = handData[j][i].value * (numCard - 1);
							}
						}
					}
				}
			}
		}

		/**
		 * xử lý tính toán phỏm dọc sau khi đã tính toán phỏm ngang
		 */
		private function postSetKindGroupRank():void
		{
			/*
			số cards trong 1 row. Nếu là 3 hoặc 4 >>> có phỏm
			*/
			var numCard:int;
			var numIntersectCard:int;
			var i:uint;
			var j:uint;
			var sumKindRemain:int; // tổng giá trị kind remain (phỏm dọc) của bộ phỏm
			var sumStraitRemain:int; // tổng giá trị rank của bộ phỏm
			var currentColumnValue:int;

			for (i = 0; i < COLUMNS; i++) // duyệt theo cột
			{
				numCard = columnsInfo[i]; // số card trong bộ = 0
				sumKindRemain = 0;
				sumStraitRemain = 0;
				numIntersectCard = 0;

				if (numCard >= 3) // có từ 3 card trở lên trong bộ >>> có phỏm
				{
					/*
					tìm tất cả intersect card (card mà có thể nằm cả ở phỏm dọc lẫn phỏm ngang)
					//*/
					for (j = 0; j < ROWS; j++)
					{
						if (handData[j][i].value != 0) // có card tại vị trí j, i
						{
							currentColumnValue = handData[j][i].value; // giống nhau ở trong cùng 1 cột
							numIntersectCard++;

							if (handData[j][i].straitRemain != 0) // có mặt trong phỏm ngang nào đó
							{
								sumStraitRemain += handData[j][i].straitRemain;
							}
						}
					}

					if ((numCard - numIntersectCard) >= 3) // số card lẻ trong bộ dọc sau khi trừ đi những các có trong bộ ngang thì là 0, 1, 2
					{
						for (j = 0; j < ROWS; j++)
						{
							if (handData[j][i].value != 0 && handData[j][i].straitRemain == 0) // có card tại vị trí j, i và không nằm trong bộ phỏm ngang
							{
								handData[j][i].kindRemain = handData[j][i].value * 2; // nằm trong bộ dọc có 3 cây >>> kind remain = 2 * giá trị của cây đó
							}
						}
					}
					else // >= 3 >>> chỉ = 3 khi có bộ tứ quý và chỉ có duy nhất 1 quân trong bộ tứ quý có nằm trong 1 phỏm ngang nào đó
					{
						sumKindRemain = (numCard - numIntersectCard) * currentColumnValue;

						if (sumKindRemain > sumStraitRemain) // nếu kind remain mà nhiều hơn thì chuyển bộ phỏm từ ngang sang dọc đồng thời cập nhật giá trị cho những quân mà nằm trong bộ ngang nào đó
						{
							for (j = 0; j < ROWS; j++)
							{
								if (handData[j][i].value != 0) // có card tại vị trí j, i
								{
									if (handData[j][i].straitRemain != 0) //  và  nằm trong bộ phỏm ngang nào đó >>> cập nhật lại giá trị cho bộ ngang đó
									{
										setLeftStraitGroupRank(j, i);
										setRightStraitGroupRank(j, i);

										handData[j][i].straitRemain = 0;
									}

									handData[j][i].kindRemain = handData[j][i].value * (numCard - 1);
								}
							}
						}
					}
				}
			}
		}

		/**
		 * set rank cho bộ phỏm ở NGAY (!) bên tay phải tại vị trí columnIndex, rowIndex
		 */
		private function setRightStraitGroupRank(rowIndex:uint, columnIndex:uint):void
		{
			var currentColumnIndex:int = columnIndex + 1; // cột ngay bên tay phải cột đưa vào để tính
			var numCard:int = 0;
			var sumValue:int = 0;

			while (currentColumnIndex <= (columnsInfo.length - 1) && handData[rowIndex][currentColumnIndex].value != 0)
			{
				handData[rowIndex][currentColumnIndex].straitRemain = 0; // gán = 0
				sumValue += handData[rowIndex][currentColumnIndex].value;
				numCard++;

				currentColumnIndex++;
			}

			if (numCard >= 3) // có phỏm
			{
				/*
				cập nhật giá trị mới cho phỏm ngang (số card cần cập nhật là numCard từ ngay trước card trống trở về trước)
				//*/
				for (var j:uint = 1; j <= numCard; j++)
				{
					handData[rowIndex][columnIndex + j].straitRemain = sumValue - handData[rowIndex][columnIndex + j].value; // trừ đi giá trị của chính card đó thì sẽ ra remain
				}
			}
		}

		/**
		 * set rank cho bộ phỏm ở NGAY (!) bên tay trái tại vị trí columnIndex, rowIndex
		 */
		private function setLeftStraitGroupRank(rowIndex:uint, columnIndex:uint):void
		{
			var currentColumnIndex:int = columnIndex - 1; // cột ngay bên tay phải cột đưa vào để tính
			var numCard:int = 0;
			var sumValue:int = 0;

			while (currentColumnIndex >= 0 && handData[rowIndex][currentColumnIndex].value != 0)
			{
				handData[rowIndex][currentColumnIndex].straitRemain = 0; // gán = 0
				sumValue += handData[rowIndex][currentColumnIndex].value;
				numCard++;

				currentColumnIndex--;
			}

			if (numCard >= 3) // có phỏm
			{
				/*
				cập nhật giá trị mới cho phỏm ngang (số card cần cập nhật là numCard từ ngay trước card trống trở về trước)
				//*/
				for (var j:uint = 1; j <= numCard; j++)
				{
					handData[rowIndex][columnIndex - j].straitRemain = sumValue - handData[rowIndex][columnIndex - j].value; // trừ đi giá trị của chính card đó thì sẽ ra remain
				}
			}
		}

		/**
		 * số bộ phỏm cùng giá trị tối đa có thể có
		 */
		private function maxKindGroups():uint
		{
			var res:uint = 0;

			for (var i:uint = 0; i < COLUMNS; i++)
			{
				if (columnsInfo[i] >= 3) // có hơn 3 cây cùng giá trị >>> phỏm
				{
					res++;
				}
			}

			return res;
		}

		/**
		 * số bộ phỏm có cùng chất tối đa có thể có
		 */
		private function maxStraitGroups():uint
		{
			var res:uint = 0;
			var numCard:int;

			for (var i:uint = 0; i < ROWS; i++)
			{
				if (rowsInfo[i] >= 3) // chỉ xét những chất mà có ít nhất từ 3 quân trở lên
				{
					numCard = 0;

					for (var j:uint = 0; j < COLUMNS; j++)
					{
						if (handData[i][j].value != 0) // nếu card rank tại cột đó !=0 thì tăng số card trong bộ lên 1
						{
							numCard++;
						}
						else // nếu không có rank tại cột đó thì chứng tỏ đó là 1 cột rỗng >>> bị ngắt quãng
						{
							res += Math.floor(numCard / 3); // nếu phỏm dài thì ưu tiên ngắt thành nhiều phỏm nhỏ hơn với độ dài 3
							numCard = 0;
						}
					}
				}
			}

			return res;
		}

		public function resetData():void
		{
			for (var i:uint = 0; i < ROWS; i++) // 4 chất
			{
				rowsInfo[i] = 0;

				for (var j:uint = 0; j < COLUMNS; j++) // 13 quân
				{
					handData[i][j].resetData();

					if (i == 0) // set 1 lần duy nhất
					{
						columnsInfo[j] = 0;
					}
				}
			}
		}

		private function cardValueToInt(cardValue:String):int
		{
			var res:int = 0;

			switch (cardValue)
			{
				case Card.ACE:
					res = 1;
					break;
				case "2":
				case "3":
				case "4":
				case "5":
				case "6":
				case "7":
				case "8":
				case "9":
				case "10":
					res = int(cardValue);
					break;
				case Card.JACK:
					res = 11;
					break;
				case Card.QUEEN:
					res = 12;
					break;
				case Card.KING:
					res = 13;
					break;
				default:
					res = -1;
					break;
			}

			return res;
		}

		private function cardSuitToInt(cardSuit:String):int
		{
			var res:int = 0;

			switch (cardSuit)
			{
				case Card.CLUBS:
					res = 1;
					break;
				case Card.DIAMONDS:
					res = 2;
					break;
				case Card.HEARTS:
					res = 3;
					break;
				case Card.SPADES:
					res = 4;
					break;
				default:
					res = -1;
					break;
			}

			return res;
		}

		public function toStringEx():String
		{
			var res:String = "";
			var rank:String = "";
			var kindRemain:String = "";
			var straitRemain:String = "";

			for (var i:uint = 0; i < ROWS; i++)
			{
				for (var j:uint = 0; j < COLUMNS; j++)
				{
					rank = (handData[i][j].value < 10) ? ("0" + handData[i][j].value.toString()) : handData[i][j].value.toString();
					kindRemain = (handData[i][j].kindRemain < 10) ? ("0" + handData[i][j].kindRemain.toString()) : handData[i][j].kindRemain.toString();
					straitRemain = (handData[i][j].straitRemain < 10) ? ("0" + handData[i][j].straitRemain.toString()) : handData[i][j].straitRemain.toString();

					res += "(" + rank + "|" + kindRemain + "-" + straitRemain + ") ";
				}
				res += "\r\n";
			}

			res += "max kind:" + maxKindGroups() + "\r\n";
			res += "max strait:" + maxStraitGroups() + "\r\n";

			return res;
		}
	}
}
